package com.yalantis.ucrop.task;

import com.yalantis.ucrop.util.NumCalcUtil;
import com.yalantis.ucrop.view.CropResult;
import ohos.aafwk.ability.DataAbilityHelper;
import ohos.app.Context;
import ohos.app.Environment;
import ohos.data.rdb.ValuesBucket;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.media.image.ImagePacker;
import ohos.media.image.ImageSource;
import ohos.media.image.PixelMap;
import ohos.agp.utils.Matrix;
import ohos.agp.utils.RectFloat;
import ohos.media.image.common.Rect;
import ohos.media.image.common.Size;
import ohos.media.photokit.metadata.AVStorage;
import ohos.utils.net.Uri;
import com.yalantis.ucrop.callback.BitmapCropCallback;
import com.yalantis.ucrop.model.CropParameters;
import com.yalantis.ucrop.model.ImageState;
import com.yalantis.ucrop.util.FileUtils;

import com.yalantis.ucrop.util.LogUtils;

import java.io.*;
import java.lang.ref.WeakReference;
import java.math.BigDecimal;

/**
 * Crops part of image that fills the crop bounds.
 * <p/>
 * First image is downscaled if max size was set and if resulting image is larger that max size.
 * Then image is rotated accordingly.
 * Finally new Bitmap object is created and saved to file.
 */
public class BitmapCropTask implements CropResult{

    private static final String TAG = "BitmapCropTask";

    private final WeakReference<Context> mContext;

    private PixelMap mViewBitmap;

    private final RectFloat mCropRect;
    private final RectFloat mCurrentImageRect;

    public float mCurrentScale;
    public final float mCurrentAngle;
    private final int mMaxResultImageSizeX, mMaxResultImageSizeY;

    public String mImageInputPath = null;
    public String mImageOutputPath = null;
    private final BitmapCropCallback mCropCallback;

    private CropResult cropResult;

    public BitmapCropTask(Context context,  PixelMap viewBitmap,  ImageState imageState,  CropParameters cropParameters,
                           BitmapCropCallback cropCallback) {

        mContext = new WeakReference<>(context);

        mViewBitmap = viewBitmap;
        mCropRect = imageState.getCropRect();
        mCurrentImageRect = imageState.getCurrentImageRect();

        mCurrentScale = imageState.getCurrentScale();
        mCurrentAngle = imageState.getCurrentAngle();
        mMaxResultImageSizeX = cropParameters.getMaxResultImageSizeX();
        mMaxResultImageSizeY = cropParameters.getMaxResultImageSizeY();

        mImageInputPath = cropParameters.getImageInputPath();
        mImageOutputPath = cropParameters.getImageOutputPath();

        mCropCallback = cropCallback;
    }

    /**
     * doInBackground
     */
    public void doInBackground(){
        EventRunner eventRunner = EventRunner.create();
        EventHandler handler = new EventHandler(eventRunner);
        handler.postTask(() -> {
            if (mViewBitmap == null) {
                Throwable t = new NullPointerException("ViewBitmap is null");
                mCropCallback.onCropFailure(t);
                return;
            } else if (mViewBitmap.isReleased()) {
                Throwable t = new NullPointerException("ViewBitmap is null");
                mCropCallback.onCropFailure(t);
                return;
            } else if (mCurrentImageRect.isEmpty()) {
                Throwable t = new NullPointerException("ViewBitmap is null");
                mCropCallback.onCropFailure(t);
                return;
            }
            try {
                crop();
                mViewBitmap = null;
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
    }

    /**
     * crop
     *
     * @throws IOException
     */
    public void crop() throws IOException {
        // Downsize if needed
        if (mMaxResultImageSizeX > 0 && mMaxResultImageSizeY > 0) {
            float cropWidth = mCropRect.getWidth() / mCurrentScale;
            float cropHeight = mCropRect.getHeight() / mCurrentScale;

            if (cropWidth > mMaxResultImageSizeX || cropHeight > mMaxResultImageSizeY) {

                float scaleX = mMaxResultImageSizeX / cropWidth;
                float scaleY = mMaxResultImageSizeY / cropHeight;
                float resizeScale = Math.min(scaleX, scaleY);

                PixelMap.InitializationOptions options = new PixelMap.InitializationOptions();
                options.size = new Size();
                options.size.width = Math.round(mViewBitmap.getImageInfo().size.width * resizeScale);
                options.size.height = Math.round(mViewBitmap.getImageInfo().size.height * resizeScale);
                PixelMap resizedBitmap = PixelMap.create(mViewBitmap, options);
                if (mViewBitmap != resizedBitmap) {
                    mViewBitmap.release();
                }
                mViewBitmap = resizedBitmap;

                mCurrentScale /= resizeScale;
            }
        }

        // Rotate if needed
        if (mCurrentAngle != 0) {
            //逻辑：将Bitmap缩放、旋转
            //逻辑：将PixelMap保存，再读取，读取时缩放并旋转.保存需要路径，所以需要Ability传进来Context

            //获取缩放和旋转参数
            Matrix tempMatrix = new Matrix();
            tempMatrix.setRotate(mCurrentAngle, mViewBitmap.getImageInfo().size.width / 2f, mViewBitmap.getImageInfo().size.height / 2f);
            PixelMap.InitializationOptions options = new PixelMap.InitializationOptions();
            options.size = new Size();
            options.size.width = mViewBitmap.getImageInfo().size.width;
            options.size.height = mViewBitmap.getImageInfo().size.height;

            //保存
            File file_path = mContext.get().getExternalFilesDir(Environment.DIRECTORY_PICTURES);
            File temp_file = new File(file_path + "/temp1.jpg");
            _saveImage(temp_file, mViewBitmap);
            LogUtils.LogInfo("qqqqqqqqqq:","_saveImage");
            //读取
            PixelMap rotatedBitmap = _loadImage(mContext.get(), temp_file, options.size, mCurrentAngle);
            LogUtils.LogInfo("qqqqqqqqqq:","_loadImage");
            LogUtils.LogInfo("qqqqqqq:","mCurrentAngle"+ mCurrentAngle);

            if (mViewBitmap != rotatedBitmap) {
                mViewBitmap.release();
            }

            mViewBitmap = rotatedBitmap;
            LogUtils.LogInfo("qqqqqqq:","mViewBitmap"+ mViewBitmap);
        }

        int cropOffsetX = Math.round((NumCalcUtil.subtract(mCropRect.left , mCurrentImageRect.left)) / mCurrentScale);
        int cropOffsetY = Math.round((NumCalcUtil.subtract(mCropRect.top , mCurrentImageRect.top)) / mCurrentScale);
//        int mCroppedImageWidth = Math.round(NumCalcUtil.divide(mCropRect.getWidth(),mCurrentScale));
//        int mCroppedImageHeight = Math.round(NumCalcUtil.divide(mCropRect.getHeight(),mCurrentScale));
        int mCroppedImageWidth = Math.round(mCropRect.getWidth() / mCurrentScale);
        int mCroppedImageHeight = Math.round(mCropRect.getHeight() / mCurrentScale);

        boolean shouldCrop = shouldCrop(mCroppedImageWidth, mCroppedImageHeight);
        LogUtils.LogInfo(TAG, "Should crop: " + shouldCrop);

        if (shouldCrop) {
//            Rect rect = new Rect(cropOffsetX, cropOffsetY, mCroppedImageWidth, mCroppedImageHeight);
            Rect rect = new Rect(cropOffsetX, cropOffsetY, mCroppedImageWidth, mCroppedImageHeight);
            PixelMap.InitializationOptions options = new PixelMap.InitializationOptions();
            LogUtils.LogInfo("qqqqqq:", mImageOutputPath);

            PixelMap pixelMap = PixelMap.create(mViewBitmap, rect, options);
            //修复错位与错切问题
            PixelMap.InitializationOptions options1 = new PixelMap.InitializationOptions();
            options1.size = new Size(mCroppedImageWidth, mCroppedImageHeight);
            PixelMap pixelMap1 = PixelMap.create(pixelMap,options1);

            saveImage(pixelMap1);
            Size size = mViewBitmap.getImageInfo().size;
            LogUtils.LogInfo("qqqqqq:", "rectmViewBitmap" + size.width+"/"+size.height);
            LogUtils.LogInfo("qqqqqq:", "rect" + cropOffsetX +"/"+ cropOffsetY +"/"+ mCroppedImageWidth +"/"+ mCroppedImageHeight);
        } else {
            FileUtils.copyFile(mImageInputPath, mImageOutputPath);
        }

    }

    /**
     * 定义回调方法
     *
     * @param result result
     */
    public void onCropResult(CropResult result) {
        this.cropResult = result;
    }

    /**
     * Check whether an image should be cropped at all or just file can be copied to the destination path.
     * For each 1000 pixels there is one pixel of error due to matrix calculations etc.
     *
     * @param width  - crop area width
     * @param height - crop area height
     * @return - true if image must be cropped, false - if original image fits requirements
     */
    private boolean shouldCrop(int width, int height) {
        int pixelError = 1;
        pixelError += Math.round(Math.max(width, height) / 1000f);
        return (mMaxResultImageSizeX > 0 && mMaxResultImageSizeY > 0)
                || Math.abs(NumCalcUtil.subtract(mCropRect.left,mCurrentImageRect.left)) > pixelError
                || Math.abs(NumCalcUtil.subtract(mCropRect.top , mCurrentImageRect.top)) > pixelError
                || Math.abs(NumCalcUtil.subtract(mCropRect.bottom , mCurrentImageRect.bottom)) > pixelError
                || Math.abs(NumCalcUtil.subtract(mCropRect.right , mCurrentImageRect.right)) > pixelError
                || mCurrentAngle != 0;
    }


    private void saveImage(PixelMap pixelMap) {
        try {
            ValuesBucket valuesBucket = new ValuesBucket();
            valuesBucket.putString(AVStorage.Images.Media.DISPLAY_NAME, "CropedFile.jpg");
            valuesBucket.putString("relative_path", "DCIM/");
            valuesBucket.putString(AVStorage.Images.Media.MIME_TYPE, "image/JPEG");
            //应用独占
            valuesBucket.putInteger("is_pending", 1);
            DataAbilityHelper helper = DataAbilityHelper.creator(mContext.get());
            int id = helper.insert(AVStorage.Images.Media.EXTERNAL_DATA_ABILITY_URI, valuesBucket);
            Uri uri = Uri.appendEncodedPathToUri(AVStorage.Images.Media.EXTERNAL_DATA_ABILITY_URI, String.valueOf(id));
            //这里需要"w"写权限
            FileDescriptor fd = helper.openFile(uri, "w");
            ImagePacker imagePacker = ImagePacker.create();
            ImagePacker.PackingOptions packingOptions = new ImagePacker.PackingOptions();
            OutputStream outputStream = new FileOutputStream(fd);

            packingOptions.format = "image/jpeg";
            packingOptions.quality = 100;
            boolean result = imagePacker.initializePacking(outputStream, packingOptions);
            if (result) {
                result = imagePacker.addImage(pixelMap);
                if (result) {
                    long dataSize = imagePacker.finalizePacking();
                    System.out.println(dataSize);
                }
            }
            outputStream.flush();
            outputStream.close();
            valuesBucket.clear();
            //解除独占
            valuesBucket.putInteger("is_pending", 0);
            helper.update(uri, valuesBucket, null);

            cropResult.onCropResult(uri.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void _saveImage(File file, PixelMap pixelMap){
        //创建ImagePacker实例
        ImagePacker imagePacker = ImagePacker.create();
        //依据路径创建数据流
        FileOutputStream outputStream = null;
        try {
            outputStream = new FileOutputStream(file);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        //创建编码设置，并为其添加规则
        ImagePacker.PackingOptions packingOptions = new ImagePacker.PackingOptions();
        packingOptions.format = "image/jpeg";
//        packingOptions.quality = 90;
        //预编码
        imagePacker.initializePacking(outputStream, packingOptions);
        //将需要编码的位图放入ImagePacker中，并完成编码
        imagePacker.addImage(pixelMap);
        imagePacker.finalizePacking();
    }

    /**
     * _loadImage
     *
     * @param context context
     * @param file file
     * @param size size
     * @param degrees degrees
     * @return _loadImage
     */
    public PixelMap _loadImage(Context context, File file, Size size, float degrees) {
        System.out.println(context);
        System.out.println(size);
        if (file == null) {
            return null;
        }
        //图片源设置
        ImageSource.SourceOptions options = new ImageSource.SourceOptions();
        options.formatHint = "image/jpg";
        //图片解码设置
        ImageSource.DecodingOptions decodingOptions = new ImageSource.DecodingOptions();
//        decodingOptions.desiredSize = size;
        decodingOptions.rotateDegrees = degrees;
        decodingOptions.editable = true;

        //使用ImageSource的create()函数，将图片读取到ImageSource实例中
        ImageSource source = ImageSource.create(file, options);
        //使用ImageSource的createPixelMap()函数，将ImageSource实例解码为位图实例
        return source.createPixelmap(decodingOptions);
    }

    @Override
    public void onCropResult(String path) {

    }
}
